package com.umakr.ax;

import com.umakr.ax.core.Constants;
import com.umakr.ax.core.config.RedissonProperties;
import com.umakr.ax.utils.CacheUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonSpringCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.*;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author gx
 * @date 2017/4/10
 * com.umakr.ax
 */
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedissonProperties.class)
@ConditionalOnBase(name = "base.redis.cache")
@ConditionalOnClass({RedissonClient.class})
public class RedisCacheConfig extends CachingConfigurerSupport {

    @SuppressWarnings("SpringJavaAutowiringInspection")
    @Autowired
    RedissonProperties redissonProperties;

    @Autowired
    RedissonClient redissonClient;

    private static final String OBJECTS_ID = "id";

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        if (StringUtils.isNotBlank(redissonProperties.getSingleAddress())) {
            SingleServerConfig serverConfig = config.useSingleServer().setAddress(redissonProperties.getSingleAddress());
            if (StringUtils.isNotBlank(redissonProperties.getPassword())) {
                serverConfig.setPassword(redissonProperties.getPassword());
            }
        } else if (!redissonProperties.getNodeAddresses().isEmpty()) {
            ClusterServersConfig serverConfig = config.useClusterServers()
                    .addNodeAddress(redissonProperties.getNodeAddresses().toArray(new String[] {}));
            if (StringUtils.isNotBlank(redissonProperties.getPassword())) {
                serverConfig.setPassword(redissonProperties.getPassword());
            }
        } else if (redissonProperties.getMasterAddress() != null && !redissonProperties.getSlaveAddresses().isEmpty()) {
            MasterSlaveServersConfig serverConfig = config.useMasterSlaveServers().setMasterAddress(redissonProperties.getMasterAddress() )
                    .addSlaveAddress(redissonProperties.getSlaveAddresses().toArray(new String[] {}));
            if (StringUtils.isNotBlank(redissonProperties.getPassword())) {
                serverConfig.setPassword(redissonProperties.getPassword());
            }
        }
        return Redisson.create(config);
    }


    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (Object o, Method method, Object... objects)-> {
                StringBuilder sb = new StringBuilder(Constants.CACHE_NAMESPACE);
                org.springframework.cache.annotation.CacheConfig cacheConfig = o.getClass().getAnnotation(org.springframework.cache.annotation.CacheConfig.class);
                Cacheable cacheable = method.getAnnotation(Cacheable.class);
                CachePut cachePut = method.getAnnotation(CachePut.class);
                CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class);
                if (cacheable != null) {
                    String[] cacheNames = cacheable.value();
                    if (cacheNames != null && cacheNames.length > 0) {
                        sb.append(cacheNames[0]);
                    }
                } else if (cachePut != null) {
                    String[] cacheNames = cachePut.value();
                    if (cacheNames != null && cacheNames.length > 0) {
                        sb.append(cacheNames[0]);
                    }
                } else if (cacheEvict != null) {
                    String[] cacheNames = cacheEvict.value();
                    if (cacheNames != null && cacheNames.length > 0) {
                        sb.append(cacheNames[0]);
                    }
                }
                if (cacheConfig != null && sb.toString().equals(Constants.CACHE_NAMESPACE)) {
                    String[] cacheNames = cacheConfig.cacheNames();
                    if (cacheNames != null && cacheNames.length > 0) {
                        sb.append(cacheNames[0]);
                    }
                }
                if (sb.toString().equals(Constants.CACHE_NAMESPACE)) {
                    sb.append(o.getClass().getName());
                }
                sb.append(":");
                if (objects != null) {
                    if (objects.length == 1) {
                        if (objects[0] instanceof Number || objects[0] instanceof String
                                || objects[0] instanceof Boolean) {
                            sb.append(objects[0]);
                        } else {
                            try {
                                sb.append(objects[0].getClass().getMethod("getId").invoke(objects[0]));
                            } catch (Exception e) {
                                if (objects[0] instanceof Map && ((Map<?, ?>) objects[0]).get(OBJECTS_ID) != null) {
                                    sb.append(((Map<?, ?>) objects[0]).get("id"));
                                } else {
                                    sb.append(JSON.toJSON(objects[0]));
                                }
                            }
                        }
                    } else {
                        sb.append(StringUtils.join(objects, ","));
                    }
                } else {
                    sb.append(method.getName());
                }
                return sb.toString();
            };
    }
    @Bean(name = "redisCacheUtil")
    public CacheUtil cacheUtil() {
        return new CacheUtil();
    }
    @Override
    @Bean
    public CacheManager cacheManager() {
        Map<String, CacheConfig> config = new HashMap<>();
        config.put("testCache", new CacheConfig(30*60*1000, 15*60*1000));
        return new RedissonSpringCacheManager(redissonClient, config);
    }
}
